home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1995 March / Macworld CD-ROM (March 1995).cdr / Updaters / Symantec C++ 6.0.1 Update / Library Updates / Standard Libraries / C sources / console.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-08-02  |  33.9 KB  |  2,010 lines  |  [TEXT/KAHL]

  1.  
  2. /*
  3.  *  console.c
  4.  *
  5.  *  Copyright (c) 1991 Symantec Corporation.  All rights reserved.
  6.  *
  7.  */
  8.  
  9. #include <MacHeaders>
  10.  
  11. /*  remove this line to work on ALL systems  */
  12. #include <PrintTraps.h>        /*  requires System 4.1  */
  13.  
  14. #ifndef __PRINTTRAPS__
  15. #include <Printing.h>
  16. #endif
  17.  
  18. #include "stdio.h"
  19. #include "stddef.h"
  20. #include "stdlib.h"
  21. #include "string.h"
  22. #include "signal.h"
  23. #include "errno.h"
  24. #include "console.h"
  25. #include "ansi_private.h"
  26.  
  27. struct __copt console_options = { 50, 10, "\pconsole", 8, 4, 9, 0, 25, 80, 1 };
  28. char __log_stdout;
  29.  
  30. static void InitConsole(void);
  31. static void ProcessEvent(void);
  32. static WindowPeek new_console(void);
  33. static void kill_console(void);
  34. static void closeecho(void);
  35. static WindowPeek cflush(FILE *);
  36.  
  37. static void console_exit(void);
  38. static int consoleio(FILE *, int);
  39. static pascal void myStdText(short, char *, Point, Point);
  40.  
  41. static void cleos(int);
  42. static void cleol(void);
  43. static void output(unsigned char *, short);
  44. static void overwrite(unsigned char *, long);
  45. static void blank(int, int);
  46. static void insert(char, int);
  47. static void pad(char, long, long);
  48. static void paste(int, int);
  49. static int setcursor(int);
  50. static TEPtr deactivate(void);
  51. static void strip(void);
  52. static char *copy(void);
  53. static void endcopy(void);
  54. static void newline(void);
  55. static void resize(void);
  56. static void use(WindowPeek);
  57.  
  58. static int open_console_driver(void);
  59. static int doClose(void);
  60. static int doControl(void);
  61. static void doCursor(void);
  62. static void doClick(EventRecord *);
  63. static void doZoom(Point, int);
  64. static void doGrow(Point);
  65. static void doSelect(EventRecord *);
  66. static int doCmdKey(int);
  67. static void doKey(int);
  68. static void doCut(void);
  69. static void doCopy(void);
  70. static void doPaste(void);
  71.  
  72. static void print_console(void);
  73. static void print(void);
  74.  
  75. static struct console {
  76.     WindowPeek            wp;
  77.     short                height;
  78.     short                width;
  79.     short                nrows;
  80.     short                ncols;
  81.     Point                cursor;
  82.     short                tabs;
  83.     TEHandle            hTE;
  84.     Point                limit;
  85.     Handle                pasteH;
  86.     long                pasteOfs;
  87.     long                pasteLen;
  88.     FILE                *echo2fp;
  89.     unsigned            raw : 1;
  90.     unsigned            cbreak : 1;
  91.     unsigned             edit : 1;
  92.     unsigned             reading : 1;
  93.     unsigned            inverse : 1;
  94.     unsigned            spool : 1;
  95. } c;
  96.  
  97. static char console_environment, noPrint, interrupted;
  98. static short console_refnum;
  99. static MenuHandle appleMenu;
  100. static WindowPeek theConsole;
  101.  
  102. struct save {
  103.     WindowPeek            console;
  104.     GrafPtr                port;
  105. };
  106. static void setup(WindowPeek, struct save *);
  107. static void restore(struct save *);
  108.  
  109. static struct {
  110.     unsigned char    *buf;
  111.     unsigned char    *ptr;
  112.     size_t            cnt;
  113.     int                min;
  114.     int                max;
  115. } in;
  116.  
  117. struct vector {
  118.     short            vJMP;
  119.     int                (*vCode)();
  120. };
  121.  
  122. #define OFS(x)        offsetof(struct drvr, x)
  123.  
  124. static struct drvr {
  125.     short            drvrFlags, drvrDelay, drvrEMask, drvrMenu;
  126.     short            drvrOpen, drvrPrime, drvrCtl, drvrStatus, drvrClose;
  127.     char            drvrName[10];
  128.     short            drvrRTS;
  129.     struct vector    vCtl, vClose;
  130. } drvr = {
  131.     0x0760, 0, 0x016A, 0,
  132.     OFS(drvrRTS), OFS(drvrRTS), OFS(vCtl), OFS(drvrRTS), OFS(vClose),
  133.     "\p.console",
  134.     0x4E75,
  135.     { 0x4EF9 }, { 0x4EF9 }
  136. }, **drvrH;
  137.  
  138. #define hiword(x)        (((short *) &(x))[0])
  139. #define loword(x)        (((short *) &(x))[1])
  140.  
  141.  
  142. /* ---------- public entry points ---------- */
  143.  
  144.  
  145. /*
  146.  *  fopenc - open a stream on a new console
  147.  *
  148.  */
  149.  
  150. FILE *
  151. fopenc(void)
  152. {
  153.     return(freopenc(NULL, __getfile()));
  154. }
  155.  
  156.  
  157. /*
  158.  *  freopenc - reopen a stream on a new or existing console
  159.  *
  160.  *  "fp" is closed, if necessary, then opened as a console.  If "fp2"
  161.  *  is NULL, a new console is created; otherwise "fp2" must refer to
  162.  *  a console, and "fp" is made to refer to the same console.
  163.  *
  164.  */
  165.  
  166. FILE *
  167. freopenc(FILE *fp2, FILE *fp)
  168. {
  169.     if (fp == NULL)
  170.         return(NULL);
  171.     if (WWExist)
  172.         InitConsole();
  173.     fclose(fp);
  174.     fp->refnum = -1;
  175.     fp->window = fp2 ? fp2->window : new_console();
  176.     setvbuf(fp, NULL, _IOLBF, BUFSIZ);
  177.     fp->proc = consoleio;
  178.     __atexit_console(console_exit);
  179.     return(fp);
  180. }
  181.  
  182.  
  183. /*
  184.  *  cgotoxy - position cursor at <x,y>
  185.  *
  186.  *  The position of the upper left corner is <1,1>.
  187.  *
  188.  *  This routine does NOT check its arguments.  Don't place the
  189.  *  cursor off-screen!
  190.  *
  191.  */
  192.  
  193. void
  194. cgotoxy(int x, int y, FILE *fp)
  195. {
  196.     struct save save;
  197.     
  198.     setup(cflush(fp), &save);
  199.     c.cursor.h = x - 1;
  200.     c.cursor.v = y - 1;
  201.     restore(&save);
  202. }
  203.  
  204.  
  205. /*
  206.  *  cgetxy - report the current cursor position
  207.  *
  208.  *  The position of the upper left corner is <1,1>.
  209.  *
  210.  */
  211.  
  212. void
  213. cgetxy(int *x, int *y, FILE *fp)
  214. {
  215.     struct save save;
  216.     
  217.     setup(cflush(fp), &save);
  218.     *x = c.cursor.h + 1;
  219.     *y = c.cursor.v + 1;
  220.     restore(&save);
  221. }
  222.  
  223.  
  224. /*
  225.  *  ccleos - clear from cursor to end of screen
  226.  *
  227.  *  The line containing the cursor, and all following lines, are erased.
  228.  *  The cursor is left at the start of the first line erased.
  229.  *
  230.  */
  231.  
  232. void
  233. ccleos(FILE *fp)
  234. {
  235.     struct save save;
  236.     
  237.     setup(cflush(fp), &save);
  238.     cleos(c.cursor.v);
  239.     restore(&save);
  240. }
  241.  
  242.  
  243. /*
  244.  *  ccleol - clear from cursor to end of line
  245.  *
  246.  */
  247.  
  248. void
  249. ccleol(FILE *fp)
  250. {
  251.     struct save save;
  252.     
  253.     setup(cflush(fp), &save);
  254.     cleol();
  255.     restore(&save);
  256. }
  257.  
  258.  
  259. /*
  260.  *  csettabs - set tab stops
  261.  *
  262.  */
  263.  
  264. void
  265. csettabs(int tabs, FILE *fp)
  266. {
  267.     struct save save;
  268.     
  269.     setup(cflush(fp), &save);
  270.     if (tabs < 1 || tabs > c.ncols)
  271.         tabs = 1;
  272.     c.tabs = tabs;
  273.     restore(&save);
  274. }
  275.  
  276.  
  277. /*
  278.  *  csetmode - set console mode
  279.  *
  280.  */
  281.  
  282. void
  283. csetmode(int mode, FILE *fp)
  284. {
  285.     struct save save;
  286.  
  287.     setup(cflush(fp), &save);
  288.     c.raw = c.cbreak = c.edit = 0;
  289.     switch (mode) {
  290.         case C_RAW:
  291.             c.raw = 1;
  292.             break;
  293.         case C_CBREAK:
  294.             c.cbreak = 1;
  295.             break;
  296.         case C_NOECHO:
  297.             break;
  298.         case C_ECHO:
  299.             c.edit = 1;
  300.             break;
  301.     }
  302.     restore(&save);
  303. }
  304.  
  305.  
  306. /*
  307.  *  cinverse - set inverse video mode
  308.  *
  309.  *  As a side effect, the entire screen is erased.  The cursor is moved
  310.  *  to the start of its line.
  311.  *
  312.  */
  313.  
  314. void
  315. cinverse(int on, FILE *fp)
  316. {
  317.     register WindowPeek wp = cflush(fp);
  318.     struct save save;
  319.  
  320.     setup(wp, &save);
  321.     if (on) {
  322.         if (!wp->port.grafProcs) {
  323.             wp->port.grafProcs = malloc(sizeof(QDProcs));
  324.             SetStdProcs(wp->port.grafProcs);
  325.             wp->port.grafProcs->textProc = (Ptr) myStdText;
  326.         }
  327.         c.inverse = 1;
  328.     }
  329.     else {
  330.         if (wp->port.grafProcs) {
  331.             free(wp->port.grafProcs);
  332.             wp->port.grafProcs = 0;
  333.         }
  334.         c.inverse = 0;
  335.     }
  336.     cleos(0);
  337.     restore(&save);
  338. }
  339.  
  340.  
  341. /*
  342.  *  cshow - show a console window
  343.  *
  344.  *  All pending output to the window is forced to appear.
  345.  *
  346.  */
  347.  
  348. void
  349. cshow(FILE *fp)
  350. {
  351.     WindowPeek wp = cflush(fp);
  352.  
  353.     if (wp != (WindowPeek) FrontWindow())
  354.         SelectWindow((WindowPtr)wp);
  355.     ShowWindow((WindowPtr)wp);
  356. }
  357.  
  358.  
  359. /*
  360.  *  chide - hide a console window
  361.  *
  362.  */
  363.  
  364. void
  365. chide(FILE *fp)
  366. {
  367.     HideWindow((WindowPtr)cflush(fp));
  368. }
  369.  
  370.  
  371. /*
  372.  *  cecho2file - echo console display to file
  373.  *
  374.  */
  375.  
  376. void
  377. cecho2file(char *s, int append, FILE *fp)
  378. {
  379.     struct save save;
  380.     
  381.     setup(cflush(fp), &save);
  382.     closeecho();
  383.     c.echo2fp = fopen(s, append ? "a" : "w");
  384.     c.spool = 0;
  385.     restore(&save);
  386. }
  387.  
  388.  
  389. /*
  390.  *  cecho2printer - echo console display to printer
  391.  *
  392.  */
  393.  
  394. void
  395. cecho2printer(FILE *fp)
  396. {
  397.     struct save save;
  398.     
  399.     setup(cflush(fp), &save);
  400.     closeecho();
  401.     c.echo2fp = tmpfile();
  402.     c.spool = 1;
  403.     restore(&save);
  404. }
  405.  
  406.  
  407. /* ---------- console management ---------- */
  408.  
  409.  
  410. /*
  411.  *  __open_std - open the std streams
  412.  *
  413.  *  This is called automatically (by "__checkfile") whenever an
  414.  *  unopened std stream is referenced.
  415.  *
  416.  */
  417.  
  418. void
  419. __open_std(void)
  420. {
  421.     FILE *fp = NULL;
  422.     char buf[40];
  423.     
  424.     if (stdin->std)
  425.         fp = freopenc(fp, stdin);
  426.     if (stdout->std)
  427.         fp = freopenc(fp, stdout);
  428.     if (stderr->std)
  429.         fp = freopenc(fp, stderr);
  430.     if (__log_stdout) {
  431.         sprintf(buf, "%#s.log", CurApName);
  432.         cecho2file(buf, 1, stdout);
  433.         console_options.pause_atexit = 0;
  434.     }
  435. }
  436.  
  437.  
  438. /*
  439.  *  InitConsole - initialize the console environment
  440.  *
  441.  */
  442.  
  443. static void
  444. InitConsole(void)
  445. {
  446.     MenuHandle menu;
  447.     int i;
  448.  
  449.         /*  initialize the Memory Manager  */
  450.     
  451.     if (ROM85 >= 0)
  452.         MaxApplZone();
  453.     for (i = 0; i < 10; i++)
  454.         MoreMasters();
  455.     
  456.         /*  initialize Quickdraw  */
  457.     
  458.     InitGraf(NewPtr(206) + 202);
  459.     
  460.         /*  initialize the Toolbox  */
  461.         
  462.     InitFonts();
  463.     InitWindows();
  464.     TEInit();
  465.     InitDialogs(0);
  466.     InitMenus();
  467.     
  468.         /*  create menus  */
  469.         
  470.     InsertMenu(appleMenu = NewMenu(1, "\p\024"), 0);
  471.     AddResMenu(appleMenu, 'DRVR');
  472.     InsertMenu(menu = NewMenu(2, "\pFile"), 0);
  473.     AppendMenu(menu, "\pQuit/Q");
  474.     InsertMenu(menu = NewMenu(3, "\pEdit"), 0);
  475.     AppendMenu(menu, "\pUndo/Z;(-;Cut/X;Copy/C;Paste/V;Clear");
  476.     DrawMenuBar();
  477.     
  478.         /*  ready to receive events  */
  479.         
  480.     FlushEvents(everyEvent, 0);
  481.     InitCursor();
  482.     console_environment = 1;
  483. }
  484.  
  485.  
  486. /*
  487.  *  ProcessEvent - handle one event
  488.  *
  489.  */
  490.  
  491. static void
  492. ProcessEvent(void)
  493. {
  494.     int key;
  495.     EventRecord event;
  496.     WindowPeek wp;
  497.     long choice;
  498.     Str255 buf;
  499.     
  500.         /*  process key from paste buffer  */
  501.  
  502.     if (c.pasteH) {
  503.         key = (unsigned char) (*c.pasteH)[c.pasteOfs++];
  504.         if (c.pasteOfs == c.pasteLen) {
  505.             DisposHandle(c.pasteH);
  506.             c.pasteH = 0;
  507.         }
  508.         if (c.inverse)
  509.             key &= 0x7F;
  510.         if (key == '\t')
  511.             key = ' ';
  512.         doKey(key);
  513.         return;
  514.     }
  515.     
  516.         /*  check for an event  */
  517.         
  518.     SystemTask();
  519.     SEvtEnb = false;
  520.     if (GetNextEvent(everyEvent, &event)) {
  521.         if (!SystemEvent(&event))
  522.             goto doEvent;
  523.     }
  524.     else if (event.what == nullEvent) {
  525.         if (FrontWindow() == 0)
  526.             InitCursor();
  527.     }
  528.     return;
  529.     
  530.         /*  handle event  */
  531.  
  532. doEvent:
  533.     if (event.what == mouseDown) {
  534.         switch (FindWindow(event.where, (WindowPtr *)&wp)) {
  535.             case inMenuBar:
  536.                 InitCursor();
  537.                 choice = MenuSelect(event.where);
  538.                 goto doMenu;
  539.             case inSysWindow:
  540.                 SystemClick(&event, (WindowPtr)wp);
  541.                 break;
  542.         }
  543.     }
  544.     return;
  545.  
  546.         /*  handle menu choice  */
  547.  
  548. doMenu:    
  549.     switch (hiword(choice)) {
  550.         case 1:        /*  Apple  */
  551.             GetItem(appleMenu, loword(choice), buf);
  552.             OpenDeskAcc(buf);
  553.             break;
  554.         case 2:        /*  File  */
  555.             console_options.pause_atexit = 0;
  556.             exit(0);
  557.             /* no return */
  558.         case 3:        /*  Edit  */
  559.             SystemEdit(loword(choice) - 1);
  560.             break;
  561.     }
  562.     HiliteMenu(0);
  563. }
  564.  
  565.  
  566. /*
  567.  *  new_console - create a new console window
  568.  *
  569.  */
  570.  
  571. static WindowPeek
  572. new_console(void)
  573. {
  574.     GrafPtr savePort;
  575.     struct console **cH;
  576.     static Rect wbox = { 100, 100, 200, 300 };
  577.     register WindowPeek wp;
  578.     Rect bounds;
  579.     FontInfo fontInfo;
  580.     register WStateData *p;
  581.     
  582.     GetPort(&savePort);
  583.     use(0);
  584.     
  585.         /*  create the window  */
  586.         
  587.     wp = (WindowPeek) NewWindow(0, &wbox, console_options.title, 0, console_options.procID, (WindowPtr) -1, 0, 0);
  588.     MoveWindow((WindowPtr)wp, console_options.left, console_options.top, 0);
  589.     SetPort((WindowPtr)(c.wp = wp));
  590.     
  591.         /*  set up the font characteristics  */
  592.         
  593.     TextFont(console_options.txFont);
  594.     TextSize(console_options.txSize);
  595.     TextFace(console_options.txFace);
  596.     GetFontInfo(&fontInfo);
  597.     c.height = fontInfo.ascent + fontInfo.descent + fontInfo.leading;
  598.     c.width = fontInfo.widMax;
  599.     
  600.         /*  set console defaults  */
  601.         
  602.     c.tabs = 8;
  603.     c.raw = c.cbreak = c.reading = c.inverse = 0;
  604.     c.edit = 1;
  605.     c.pasteH = 0;
  606.     c.echo2fp = 0;
  607.     
  608.         /*  set initial window size  */
  609.     
  610.     bounds.top = bounds.left = 0;
  611.     bounds.bottom = (c.nrows = console_options.nrows) * c.height + 8;
  612.     bounds.right = (c.ncols = console_options.ncols) * c.width + 8;
  613.     SizeWindow((WindowPtr)wp, bounds.right, bounds.bottom, 0);
  614.     
  615.         /*  create TE record  */
  616.         
  617.     c.hTE = TENew(&bounds, &bounds);
  618.     (**c.hTE).crOnly = -1;
  619.     c.cursor.v = c.nrows - 1;
  620.     cleos(0);
  621.     
  622.         /*  set up grow/zoom parameters  */
  623.     
  624.     c.limit = botRight(bounds);
  625.     ++c.limit.v, ++c.limit.h;
  626.     LocalToGlobal(&topLeft(bounds));
  627.     LocalToGlobal(&botRight(bounds));
  628.     p = * (WStateData **) wp->dataHandle;
  629.     p->userState = p->stdState = bounds;
  630.     
  631.         /*  associate window with console  */
  632.     
  633.     asm {
  634.         lea        c,a0
  635.         move.l    #sizeof(c),d0
  636.         _PtrToHand
  637.         move.l    a0,offsetof(WindowRecord,refCon)(wp)
  638.     }
  639.     if (!console_refnum)
  640.         console_refnum = open_console_driver();
  641.     wp->windowKind = console_refnum;
  642.     
  643.         /*  done  */
  644.     
  645.     resize();
  646.     ShowWindow((WindowPtr)wp);
  647.     SetPort(savePort);
  648.     return(wp);
  649. }
  650.  
  651.  
  652. /*
  653.  *  kill_console - close a console window
  654.  *
  655.  *  The console is not closed if more than one stream shares it.
  656.  *
  657.  */
  658.  
  659. static void
  660. kill_console(void)
  661. {
  662.     register FILE *fp;
  663.     int i = 0, n;
  664.     
  665.         /*  see if more than one stream shares this console  */
  666.         
  667.     for (fp = &__file[0], n = FOPEN_MAX; n--; fp++) {
  668.         if (fp->window == c.wp && i++)
  669.             return;
  670.     }
  671.     
  672.         /*  close echo file (if any)  */
  673.  
  674.     closeecho();
  675.     
  676.         /*  discard console data structures  */
  677.         
  678.     if (c.pasteH)
  679.         DisposHandle(c.pasteH);
  680.     DisposHandle((Handle) c.wp->refCon);
  681.     TEDispose(c.hTE);
  682.     DisposeWindow((WindowPtr)c.wp);
  683.     c.wp = 0;
  684. }
  685.  
  686.  
  687. /*
  688.  *  closeecho - close echo file (if any)
  689.  *
  690.  */
  691.  
  692. static void
  693. closeecho(void)
  694. {
  695.     if (c.echo2fp) {
  696.         if (c.spool)
  697.             print_console();
  698.         fclose(c.echo2fp);
  699.     }
  700. }
  701.  
  702.  
  703. /*
  704.  *  cflush - flush all pending output to a console
  705.  *
  706.  */
  707.  
  708. static WindowPeek
  709. cflush(FILE *fp)
  710. {
  711.     WindowPeek wp = __checkfile(fp)->window;
  712.     int n;
  713.     
  714.     for (fp = &__file[0], n = FOPEN_MAX; n--; fp++) {
  715.         if (fp->dirty && fp->window == wp)
  716.             fflush(fp);
  717.     }
  718.     return(wp);
  719. }
  720.  
  721.  
  722. /* ---------- vectored entry points ---------- */
  723.  
  724.  
  725. /*
  726.  *  console_exit - console shutdown routine
  727.  *
  728.  */
  729.  
  730. static void
  731. console_exit(void)
  732. {
  733.     register FILE *fp;
  734.     int n;
  735.     
  736.         /*  complete pending output  */
  737.         
  738.     for (fp = &__file[0], n = FOPEN_MAX; n--; fp++) {
  739.         if (fp->dirty && fp->window)
  740.             fflush(fp);
  741.     }
  742.     
  743.         /*  pause for user acknowledgement  */
  744.     
  745.     if (console_environment && console_options.pause_atexit) {
  746.         for (fp = &__file[0], n = FOPEN_MAX; n--; fp++) {
  747.             if (fp->window) {
  748.                 SetWTitle(fp->window, "\ppress «return» to exit");
  749.                 c.raw = c.cbreak = c.edit = 0;
  750.                 setbuf(fp, NULL);
  751.                 fgetc(fp);
  752.                 break;
  753.             }
  754.         }
  755.     }
  756.     
  757.         /*  close consoles  */
  758.  
  759.     for (fp = &__file[0], n = FOPEN_MAX; n--; fp++) {
  760.         if (fp->window)
  761.             fclose(fp);
  762.     }
  763. }
  764.  
  765.  
  766. /*
  767.  *  consoleio - I/O handler proc for console windows
  768.  *
  769.  */
  770.  
  771. static int
  772. consoleio(FILE *fp, int i)
  773. {
  774.     struct save save;
  775.     int result = 0;
  776.     
  777.     if (_abnormal_exit)
  778.         return(0);
  779.     setup(fp->window, &save);
  780.     switch (i) {
  781.     
  782.                 /*  read  */
  783.             
  784.         case 0:
  785.             in.buf = in.ptr = fp->ptr;
  786.             if (console_environment) {
  787.                 cshow(fp);
  788.                 c.reading = 1;
  789.                 in.cnt = fp->cnt;
  790.                 if (c.edit && c.cursor.h + in.cnt > c.ncols)
  791.                     in.cnt = c.ncols - c.cursor.h + 1;
  792.                 in.min = in.max = c.raw ? 0 : setcursor(0);
  793.                 fp->eof = 0;
  794.                 do {
  795.                     ProcessEvent();
  796.                 } while (in.cnt && !c.raw);
  797.                 c.reading = 0;
  798.             }
  799.             if ((fp->cnt = in.ptr - in.buf) == 0) {
  800.                 fp->eof = 1;
  801.                 result = EOF;
  802.             }
  803.             break;
  804.             
  805.                 /*  write  */
  806.  
  807.         case 1:
  808.             output(fp->ptr, fp->cnt);
  809.             break;
  810.             
  811.                 /*  close  */
  812.  
  813.         case 2:
  814.             kill_console();
  815.             if (fp->window == save.console)
  816.                 save.console = 0;
  817.             break;
  818.     }
  819.     
  820.         /*  check for interrupt  */
  821.         
  822.     if (interrupted) {
  823.         interrupted = 0;
  824.         FlushEvents(keyDownMask, 0);
  825.         fp->cnt = 0;
  826.         raise(SIGINT);
  827.         errno = EINTR;
  828.         result = EOF;
  829.     }
  830.  
  831.         /*  done  */
  832.     
  833.     restore(&save);
  834.     return(result);
  835. }
  836.  
  837.  
  838. /*
  839.  *  myStdText - inverse video handler
  840.  *
  841.  */
  842.  
  843. static pascal void
  844. myStdText(register short n, register char *s, Point numer, Point denom)
  845. {
  846.     register char *t;
  847.     char buf;
  848.     
  849.     while (n--) {
  850.         t = s;
  851.         asm {
  852. @1            tst.b    (s)+
  853. @2            dbmi    n,@1
  854.             bpl.s    @3
  855.             subq.l    #1,s
  856. @3        }
  857.         if (s > t)
  858.             StdText(s - t, t, numer, denom);
  859.         if (n < 0)
  860.             break;
  861.         buf = *s++ & 0x7F;
  862.         TextMode(notSrcCopy);
  863.         StdText(1, &buf, numer, denom);
  864.         TextMode(srcCopy);
  865.     }
  866. }
  867.  
  868.  
  869. /* ---------- I/O primitives ---------- */
  870.  
  871.  
  872. /*
  873.  *  cleos - clear to end of screen
  874.  *
  875.  *  The given line, and all following lines, are erased.  The cursor is
  876.  *  moved to the start of its line.
  877.  *
  878.  */
  879.  
  880. static void
  881. cleos(int i)
  882. {
  883.     pad('\r', 0, c.nrows - i);
  884.     paste((**c.hTE).lineStarts[i], (**c.hTE).teLength);
  885.     c.cursor.h = 0;
  886. }
  887.  
  888.  
  889. /*
  890.  *  cleol - clear to end of line
  891.  *
  892.  */
  893.  
  894. static void
  895. cleol(void)
  896. {
  897.     register TEPtr pTE = deactivate();
  898.     register short *line = &pTE->lineStarts[c.cursor.v];
  899.     register int selStart = line[0] + c.cursor.h;
  900.     register int selEnd = line[1] - 1;
  901.  
  902.     if (selStart < selEnd) {
  903.         pTE->selStart = selStart;
  904.         pTE->selEnd = selEnd;
  905.         TEDelete(c.hTE);
  906.     }
  907. }
  908.  
  909.  
  910. /*
  911.  *  output - write characters to the current console
  912.  *
  913.  */
  914.  
  915. static void
  916. output(register unsigned char *s, register short n)
  917. {
  918.     unsigned char *t;
  919.     register EvQElPtr q;
  920.     
  921.     while (n--) {
  922.         t = s;
  923.         asm {
  924.             moveq    #' ',d0
  925. @1            cmp.b    (s)+,d0
  926. @2            dbhi    n,@1
  927.             bls.s    @3
  928.             subq.l    #1,s
  929. @3        }
  930.         if (s > t)
  931.             overwrite(t, s - t);
  932.         if (n < 0)
  933.             break;
  934.         if (!c.raw) {
  935.             for (q = (EvQElPtr) EventQueue.qHead; q; q = (EvQElPtr) q->qLink) {
  936.                 if (q->evtQWhat == keyDown && (char) q->evtQMessage == '.') {
  937.                     if (q->evtQModifiers & cmdKey) {
  938.                         interrupted = 1;
  939.                         return;
  940.                     }
  941.                 }
  942.             }
  943.         }
  944.         switch (*s++) {
  945.             case '\a':
  946.                 SysBeep(4);
  947.                 break;
  948.             case '\b':
  949.                 deactivate();
  950.                 if (c.cursor.h)
  951.                     --c.cursor.h;
  952.                 break;
  953.             case '\f':
  954.                 c.cursor.v = 0;
  955.                 cleos(0);
  956.                 break;
  957.             case '\n':
  958.                 newline();
  959.                 break;
  960.             case '\v':
  961.                 if (++c.cursor.v == c.nrows)
  962.                     --c.cursor.v;
  963.                 break;
  964.             case '\r':
  965.                 c.cursor.h = 0;
  966.                 break;
  967.             case '\t':
  968.                 do {
  969.                     ++c.cursor.h;
  970.                 } while (c.cursor.h % c.tabs);
  971.                 if (c.cursor.h > c.ncols)
  972.                     c.cursor.h = c.ncols;
  973.                 break;
  974.         }
  975.     }
  976. }
  977.  
  978.  
  979. /*
  980.  *  overwrite - place new text on a line
  981.  *
  982.  *  The text must not contain any control characters.  Text is drawn
  983.  *  starting at the current cursor, overwriting any existing characters.
  984.  *  The cursor is left at the end of the new text.
  985.  *
  986.  */
  987.  
  988. static void
  989. overwrite(unsigned char *s, long n)
  990. {
  991.     register long m;
  992.     register short *line, selStart, selEnd;
  993.     
  994.         /*  wrap output at "ncols"  */
  995.  
  996. more:    
  997.     if (c.cursor.h + (m = n) > c.ncols)
  998.         m = c.ncols - c.cursor.h;
  999.     
  1000.         /*  set replacement range  */
  1001.         
  1002.     line = &(**c.hTE).lineStarts[c.cursor.v];
  1003.     selStart = line[0] + c.cursor.h;
  1004.     selEnd = line[1] - 1;
  1005.     if (selStart > selEnd) {    /*  pad line with blanks  */
  1006.         pad(' ', 0, selStart - selEnd);
  1007.         paste(selEnd, selEnd);
  1008.         selEnd = selStart;
  1009.     }
  1010.     else if (selEnd > selStart + m)
  1011.         selEnd = selStart + m;
  1012.     
  1013.         /*  paste in new text  */
  1014.     
  1015.     PtrToXHand(s, TEScrpHandle, m);
  1016.     TEScrpLength = m;
  1017.     paste(selStart, selEnd);
  1018.     
  1019.         /*  wrap to next line if necessary  */
  1020.         
  1021.     if (m < n) {
  1022.         newline();
  1023.         s += m;
  1024.         n -= m;
  1025.         goto more;
  1026.     }
  1027.     c.cursor.h += m;
  1028. }
  1029.  
  1030.  
  1031. /*
  1032.  *  blank - erase the indicated portion of the input field
  1033.  *
  1034.  */
  1035.  
  1036. static void
  1037. blank(int selStart, int selEnd)
  1038. {
  1039.     register TEPtr pTE = deactivate();
  1040.     
  1041.     if (in.max + 1 == pTE->lineStarts[c.cursor.v + 1]) {
  1042.         pTE->selStart = selStart;
  1043.         pTE->selEnd = selEnd;
  1044.         TEDelete(c.hTE);
  1045.     }
  1046.     else {
  1047.         pTE->selStart = selEnd;
  1048.         pTE->selEnd = in.max;
  1049.         TECopy(c.hTE);
  1050.         pad(' ', in.max - selEnd, in.max - selStart);
  1051.         paste(selStart, in.max);
  1052.     }
  1053.     in.max -= selEnd - selStart;
  1054. }
  1055.  
  1056.  
  1057. /*
  1058.  *  insert - insert a character at the indicated point
  1059.  *
  1060.  */
  1061.  
  1062. static void
  1063. insert(char ch, int selStart)
  1064. {
  1065.     register TEPtr pTE = deactivate();
  1066.     
  1067.     pTE->selStart = selStart;
  1068.     if (in.max + 1 == pTE->lineStarts[c.cursor.v + 1]) {
  1069.         pTE->selEnd = selStart;
  1070.         TEKey(ch, c.hTE);
  1071.     }
  1072.     else {
  1073.         pTE->selEnd = in.max;
  1074.         TECopy(c.hTE);
  1075.         Munger(TEScrpHandle, 0, 0, 0, &ch, 1);
  1076.         ++TEScrpLength;
  1077.         paste(selStart, in.max + 1);
  1078.     }
  1079.     in.max++;
  1080. }
  1081.  
  1082.  
  1083. /*
  1084.  *  pad - pad the TE scrap to a desired size
  1085.  *
  1086.  */
  1087.  
  1088. static void
  1089. pad(register char ch, register long ofs, register long len)
  1090. {
  1091.         /*  set scrap size  */
  1092.         
  1093.     asm {
  1094.         movea.l    TEScrpHandle,a0
  1095.         move.l    len,d0
  1096.         move.w    d0,TEScrpLength
  1097.         _SetHandleSize
  1098.     }
  1099.     
  1100.         /*  fill scrap  */
  1101.         
  1102.     asm {
  1103.         movea.l    (a0),a0
  1104.         adda.l    ofs,a0
  1105.         sub.l    ofs,len
  1106.         bra.s    @2
  1107. @1        move.b    ch,(a0)+
  1108. @2        dbra    len,@1
  1109.     }
  1110. }
  1111.  
  1112.  
  1113. /*
  1114.  *  paste - replace range with contents of TEScrap
  1115.  *
  1116.  */
  1117.  
  1118. static void
  1119. paste(int selStart, int selEnd)
  1120. {
  1121.     register TEPtr pTE = deactivate();
  1122.     
  1123.     pTE->selStart = selStart;
  1124.     pTE->selEnd = selEnd;
  1125.     TEPaste(c.hTE);
  1126. }
  1127.  
  1128.  
  1129. /*
  1130.  *  setcursor - activate flashing caret at cursor
  1131.  *
  1132.  */
  1133.  
  1134. static int
  1135. setcursor(int sel)
  1136. {
  1137.     register TEPtr pTE = deactivate();
  1138.     register short *line = &pTE->lineStarts[c.cursor.v];
  1139.     register int end = line[1] - 1;
  1140.     
  1141.     sel += line[0] + c.cursor.h;
  1142.     if (sel > end) {        /*  pad line with blanks  */
  1143.         pad(' ', 0, sel - end);
  1144.         paste(end, end);
  1145.         pTE = *c.hTE;
  1146.     }
  1147.     pTE->selStart = pTE->selEnd = sel;
  1148.     pTE->clikStuff = 255;    /* per TN127 */
  1149.     TEActivate(c.hTE);
  1150.     return(sel);
  1151. }
  1152.  
  1153.  
  1154. /*
  1155.  *  deactivate - make sure TE record is inactive
  1156.  *
  1157.  *  The dereferenced pointer to the TE record is returned.
  1158.  *
  1159.  */
  1160.  
  1161. static TEPtr
  1162. deactivate(void)
  1163. {
  1164.     if ((**c.hTE).active)
  1165.         TEDeactivate(c.hTE);
  1166.     return(*c.hTE);
  1167. }
  1168.  
  1169.  
  1170. /*
  1171.  *  strip - strip trailing blanks from all lines
  1172.  *
  1173.  *  Note that we don't strip blanks that are part of the current
  1174.  *  input field.
  1175.  *
  1176.  */
  1177.  
  1178. static void
  1179. strip(void)
  1180. {
  1181.     register int i = c.nrows, j, k;
  1182.     register TEPtr pTE = *c.hTE;
  1183.     register char *p;
  1184.     
  1185.     while (i) {
  1186.         j = k = pTE->lineStarts[i--] - 1;
  1187.         for (p = *pTE->hText + j; j && *--p == ' '; j--)
  1188.             ;
  1189.         if (c.reading && !c.raw && i == c.cursor.v && j < in.max)
  1190.             j = in.max;
  1191.         if (k -= j) {
  1192.             Munger(pTE->hText, j, 0, k, "", 0);
  1193.             pTE = *c.hTE;
  1194.             if (c.reading) {
  1195.                 if (in.min > j)
  1196.                     in.min -= k;
  1197.                 if (in.max > j)
  1198.                     in.max -= k;
  1199.             }
  1200.             if (pTE->selStart > j)
  1201.                 pTE->selStart -= k;
  1202.             if (pTE->selEnd > j)
  1203.                 pTE->selEnd -= k;
  1204.         }
  1205.     }
  1206.     TECalText(c.hTE);
  1207. }
  1208.  
  1209.  
  1210. /*
  1211.  *  resize - respond to new window size
  1212.  *
  1213.  *  If the window is too small to display the entire console, the lower
  1214.  *  left portion of the console is displayed.
  1215.  *
  1216.  */
  1217.  
  1218. static void
  1219. resize(void)
  1220. {
  1221.     Rect bounds;
  1222.     
  1223.     bounds = c.wp->port.portRect;
  1224.     InvalRect(&bounds);
  1225.     InsetRect(&bounds, 4, 4);
  1226.     (**c.hTE).viewRect = bounds;
  1227.     bounds.top = bounds.bottom - c.height * c.nrows;
  1228.     (**c.hTE).destRect = bounds;
  1229. }
  1230.  
  1231.  
  1232. /*
  1233.  *  setup - focus on the appropriate console
  1234.  *
  1235.  *  The current console and the current grafport are set appropriately,
  1236.  *  and the previous values are saved.  Any pending update is performed.
  1237.  *
  1238.  */
  1239.  
  1240. static void
  1241. setup(WindowPeek wp, struct save *save)
  1242. {
  1243.     Rect bounds;
  1244.     
  1245.     GetPort(&save->port);
  1246.     save->console = theConsole;
  1247.     if (wp && wp->windowKind == console_refnum) {
  1248.         use(wp);
  1249.         SetPort((WindowPtr)wp);
  1250.         if (!EmptyRgn(wp->updateRgn)) {
  1251.             bounds = wp->port.portRect;
  1252.             BeginUpdate((WindowPtr)wp);
  1253.             EraseRect(&bounds);
  1254.             TEUpdate(&bounds, c.hTE);
  1255.             EndUpdate((WindowPtr)wp);
  1256.         }
  1257.         theConsole = wp;
  1258.     }
  1259. }
  1260.  
  1261.  
  1262. /*
  1263.  *  restore - done with the current console
  1264.  *
  1265.  *  The console and grafport saved by a previous "setup" call are restored.
  1266.  *
  1267.  */
  1268.  
  1269. static void
  1270. restore(struct save *save)
  1271. {
  1272.     if (theConsole = save->console)
  1273.         use(save->console);
  1274.     SetPort(save->port);
  1275. }
  1276.  
  1277.  
  1278. /*
  1279.  *  use - load global "c" from window's refCon
  1280.  *
  1281.  */
  1282.  
  1283. static void
  1284. use(WindowPeek wp)
  1285. {
  1286.     if (wp != c.wp) {
  1287.         if (c.wp)
  1288.             ** (struct console **) c.wp->refCon = c;
  1289.         if (wp)
  1290.             c = ** (struct console **) wp->refCon;
  1291.     }
  1292. }
  1293.  
  1294.  
  1295. /*
  1296.  *  copy - get a pointer to the text in the TE scrap
  1297.  *
  1298.  *  If inverse video is in use, the high bit is stripped from each byte.
  1299.  *  This call must be balanced by a call to "endcopy".
  1300.  *
  1301.  */
  1302.  
  1303. static char *
  1304. copy(void)
  1305. {
  1306.     asm {
  1307.         movea.l    TEScrpHandle,a0
  1308.         _HLock
  1309.         move.l    (a0),d0
  1310.     }
  1311.     if (c.inverse)
  1312.         asm {
  1313.             movea.l    d0,a1
  1314.             move.w    TEScrpLength,d1
  1315.             bra.s    @2
  1316.     @1        tst.b    (a1)+
  1317.             bpl.s    @2
  1318.             bclr    #7,-1(a1)
  1319.     @2        dbra    d1,@1
  1320.         }
  1321. }
  1322.  
  1323.  
  1324. /*
  1325.  *  endcopy - done using text obtained by "copy"
  1326.  *
  1327.  */
  1328.  
  1329. static void
  1330. endcopy(void)
  1331. {
  1332.     HUnlock(TEScrpHandle);
  1333. }
  1334.  
  1335.  
  1336. /*
  1337.  *  newline - print a newline character
  1338.  *
  1339.  */
  1340.  
  1341. static void
  1342. newline(void)
  1343. {
  1344.     register TEPtr pTE = deactivate();
  1345.     register short *line, delta, len;
  1346.     Rect bounds;
  1347.     RgnHandle rgn;
  1348.     Handle hText;
  1349.     EventRecord event;
  1350.     
  1351.     if (c.reading && !c.edit && !c.cbreak)
  1352.         return;
  1353.  
  1354.         /*  wait while mouse is down  */
  1355.  
  1356.     if (GetOSEvent(mDownMask, &event)) {
  1357.         while (!GetOSEvent(mUpMask, &event))
  1358.             ;
  1359.     }
  1360.     
  1361.         /*  copy current line to echo-file  */
  1362.         
  1363.     if (c.echo2fp) {
  1364.         line = &pTE->lineStarts[c.cursor.v];
  1365.         pTE->selStart = line[0];
  1366.         pTE->selEnd = line[1];
  1367.         TECopy(c.hTE);
  1368.         fwrite(copy(), 1, TEScrpLength, c.echo2fp);
  1369.         endcopy();
  1370.     }
  1371.  
  1372.         /*  advance cursor, scrolling if necessary  */
  1373.  
  1374.     if (++c.cursor.v == c.nrows) {
  1375.         pTE = *c.hTE;
  1376.         hText = pTE->hText;
  1377.         len = pTE->teLength -= delta = pTE->lineStarts[1];
  1378.         ++pTE->teLength;
  1379.         bounds = pTE->destRect;
  1380.         ScrollRect(&bounds, 0, -c.height, rgn = NewRgn());
  1381.         DisposeRgn(rgn);
  1382.         Munger(hText, 0, 0, delta, "", 0);
  1383.         Munger(hText, len, 0, 0, "\r", 1);
  1384.         TECalText(c.hTE);
  1385.         --c.cursor.v;
  1386.     }
  1387.     c.cursor.h = 0;
  1388. }
  1389.  
  1390.  
  1391. /* ---------- console driver ---------- */
  1392.  
  1393.  
  1394. /*
  1395.  *  open_console_driver - install and initialize the console driver
  1396.  *
  1397.  */
  1398.  
  1399. static int
  1400. open_console_driver(void)
  1401. {
  1402.     short i;
  1403.     DCtlHandle dceH;
  1404.     register DCtlPtr dce;
  1405.     
  1406.         /*  create driver on heap  */
  1407.     
  1408.     if (!drvrH) {
  1409.         drvr.vCtl.vCode = doControl;
  1410.         drvr.vClose.vCode = doClose;
  1411.         asm {
  1412.             lea        drvr,a0
  1413.             move.l    #sizeof(drvr),d0
  1414.             _PtrToHand
  1415.             move.l    a0,drvrH
  1416.         }
  1417.     }
  1418.     
  1419.         /*  find available slot in unit table  */
  1420.         
  1421.     for (i = 27; dceH = UTableBase[i]; i++) {
  1422.         if (!((**dceH).dCtlFlags & dOpened))
  1423.             break;
  1424.     }
  1425.     i = ~i;
  1426.     
  1427.         /*  create DCE  */
  1428.         
  1429.     asm {
  1430.         move.w    i,d0
  1431.         dc.w    0xA13D            ;  _DrvrInstall
  1432.         movea.l    (a0),dce
  1433.     }
  1434.     dce->dCtlDriver = (Ptr) drvrH;
  1435.     dce->dCtlFlags = drvr.drvrFlags;
  1436.     dce->dCtlEMask = drvr.drvrEMask;
  1437.     return(i);
  1438. }
  1439.  
  1440.  
  1441. /*
  1442.  *  doClose - handle _PBClose call
  1443.  *
  1444.  *  Normally, we shouldn't get a close call, because a console window has
  1445.  *  no go-away box.  If we do reach here, we can keep from crashing (except
  1446.  *  on 64K ROMs) by refusing to close.
  1447.  *
  1448.  */
  1449.  
  1450. static int
  1451. doClose(void)
  1452. {
  1453.     return(closErr);
  1454. }
  1455.  
  1456.  
  1457. /*
  1458.  *  doControl - handle _PBControl call
  1459.  *
  1460.  */
  1461.  
  1462. static int
  1463. doControl(void)
  1464. {
  1465.     register CntrlParam *pb;
  1466.     DCtlPtr dce;
  1467.     struct save save, save2;
  1468.     register EventRecord *event;
  1469.     int key;
  1470.     long oldA5 = SetCurrentA5();
  1471.     
  1472.     asm {
  1473.         move.l    a0,pb
  1474.         move.l    a1,dce
  1475.     }
  1476.     setup((WindowPeek) FrontWindow(), &save);
  1477.     switch (pb->csCode) {
  1478.         case accCursor:
  1479.             doCursor();
  1480.             break;
  1481.         case accCut:
  1482.             doCut();
  1483.             break;
  1484.         case accCopy:
  1485.             doCopy();
  1486.             break;
  1487.         case accPaste:
  1488.             doPaste();
  1489.             break;
  1490.         case accClear:
  1491.             doKey('\33');
  1492.             break;
  1493.         case accEvent:
  1494.             event = * (EventRecord **) pb->csParam;
  1495.             switch (event->what) {
  1496.                 case updateEvt:
  1497.                     setup((WindowPeek) event->message, &save2);
  1498.                     break;
  1499.                 case mouseDown:
  1500.                     doClick(event);
  1501.                     break;
  1502.                 case keyDown:
  1503.                 case autoKey:
  1504.                     key = (unsigned char) event->message;
  1505.                     if (event->modifiers & cmdKey) {
  1506.                         if (event->what == autoKey)
  1507.                             break;
  1508.                         key = doCmdKey(key);
  1509.                     }
  1510.                     if (key)
  1511.                         doKey(key);
  1512.                     break;
  1513.             }
  1514.             break;
  1515.     }
  1516.     HUnlock((Handle)drvrH);
  1517.     HUnlock(RecoverHandleSys((Ptr)dce));
  1518.     restore(&save);
  1519.     SetA5(oldA5);
  1520.     return(0);
  1521. }
  1522.  
  1523.  
  1524. /*
  1525.  *  doCursor - cursor maintenance
  1526.  *
  1527.  */
  1528.  
  1529. static void
  1530. doCursor(void)
  1531. {
  1532.     Point where;
  1533.     
  1534.     TEIdle(c.hTE);
  1535.     GetMouse(&where);
  1536.     if (PtInRect(where, &c.wp->port.portRect))
  1537.         SetCursor(*GetCursor(iBeamCursor));
  1538.     else asm {
  1539.         movea.l    (a5),a0
  1540.         pea        -108(a0)            ;  arrow
  1541.         _SetCursor
  1542.     }
  1543. }
  1544.  
  1545.  
  1546. /*
  1547.  *  doClick - handle mouse-down event
  1548.  *
  1549.  *  Since the click was passed to the console driver, the front window
  1550.  *  must be a console window, and the click can only be in its zoom box
  1551.  *  or content region (including the grow region).
  1552.  *
  1553.  */
  1554.  
  1555. static void
  1556. doClick(EventRecord *event)
  1557. {
  1558.     int part;
  1559.     
  1560.     c.wp->windowKind = userKind;
  1561.     part = FindWindow(event->where, (WindowPtr *)&c.wp);
  1562.     c.wp->windowKind = console_refnum;
  1563.     switch (part) {
  1564.         case inZoomIn:
  1565.         case inZoomOut:
  1566.             doZoom(event->where, part);
  1567.             break;
  1568.         case inGrow:
  1569.             if (!(event->modifiers & (cmdKey|optionKey))) {
  1570.                 doGrow(event->where);
  1571.                 break;
  1572.             }
  1573.             /* ... */
  1574.         case inContent:
  1575.             doSelect(event);
  1576.             break;
  1577.     }
  1578. }
  1579.  
  1580.  
  1581. /*
  1582.  *  doZoom - handle click in zoom box
  1583.  *
  1584.  *  The "zoomed" size is that needed to display the entire console.
  1585.  *
  1586.  */
  1587.  
  1588. static void
  1589. doZoom(Point where, int part)
  1590. {
  1591.     register WindowPeek wp = c.wp;
  1592.  
  1593.     InitCursor();
  1594.     if (TrackBox((WindowPtr)wp, where, part)) {
  1595.         EraseRect(&wp->port.portRect);
  1596.         ZoomWindow((WindowPtr)wp, part, 0);
  1597.         resize();
  1598.     }
  1599. }
  1600.  
  1601.  
  1602. /*
  1603.  *  doGrow - handle click in grow region
  1604.  *
  1605.  *  No grow box is actually visible; nonetheless, clicking in the lower
  1606.  *  right corner has the same effect.  Hold down the command or option
  1607.  *  key to defeat this behavior (e.g. to select corner text).
  1608.  *
  1609.  *  The maximum window size is that needed to display the entire console.
  1610.  *
  1611.  */
  1612.  
  1613. static void
  1614. doGrow(Point where)
  1615. {
  1616.     register WindowPeek wp = c.wp;
  1617.     long size;
  1618.     static Rect limit = { 100, 120, 9999, 9999 };
  1619.  
  1620.     InitCursor();
  1621.     botRight(limit) = c.limit;
  1622.     if (size = GrowWindow((WindowPtr)wp, where, &limit)) {
  1623.         EraseRect(&wp->port.portRect);
  1624.         SizeWindow((WindowPtr)wp, loword(size), hiword(size), 0);
  1625.         resize();
  1626.     }
  1627. }
  1628.  
  1629.  
  1630. /*
  1631.  *  doSelect - handle click in content region, initiating selection
  1632.  *
  1633.  */
  1634.  
  1635. static void
  1636. doSelect(EventRecord *event)
  1637. {
  1638.     int extend = 0;
  1639.     register TEPtr pTE;
  1640.     
  1641.     if (!(**c.hTE).active)
  1642.         setcursor(0);
  1643.     else if (event->modifiers & shiftKey)
  1644.         extend = 1;
  1645.     strip();
  1646.     
  1647.         /*  let TE do the work  */
  1648.         
  1649.     GlobalToLocal(&event->where);
  1650.     TEClick(event->where, extend, c.hTE);
  1651.     pTE = *c.hTE;
  1652.     
  1653.         /*  fix selection  */
  1654.         
  1655.     if (pTE->selStart == pTE->selEnd) {
  1656.         pTE->clikStuff = 255;    /* per TN127 */
  1657.         if (!c.reading || c.raw)
  1658.             TEDeactivate(c.hTE);
  1659.         else if (pTE->selStart < in.min)
  1660.             TESetSelect(in.min, in.min, c.hTE);
  1661.         else if (pTE->selEnd > in.max)
  1662.             TESetSelect(in.max, in.max, c.hTE);
  1663.     }
  1664. }
  1665.  
  1666.  
  1667. /*
  1668.  *  doCmdKey - handle command key
  1669.  *
  1670.  */
  1671.  
  1672. static int
  1673. doCmdKey(int key)
  1674. {
  1675.     if (c.raw)
  1676.         return(key & 0x1F);        /*  controlify  */
  1677.     switch (key) {
  1678.         case 'x': case 'X':
  1679.             doCut();
  1680.             break;
  1681.         case 'c': case 'C':
  1682.             doCopy();
  1683.             break;
  1684.         case 'v': case 'V':
  1685.             doPaste();
  1686.             break;
  1687.         case '.':
  1688.             if (console_environment)
  1689.                 interrupted = 1;
  1690.             /* ... */
  1691.         case 'd': case 'D':
  1692.             return('\4');        /*  ctrl-D  */
  1693.         case 'u': case 'U':
  1694.         case 'z': case 'Z':
  1695.             return('\25');        /*  ctrl-U  */
  1696.         case 'q': case 'Q':
  1697.             if (console_environment) {
  1698.                 console_options.pause_atexit = 0;
  1699.                 exit(0);
  1700.             }
  1701.             break;
  1702.     }
  1703.     return(0);
  1704. }
  1705.  
  1706.  
  1707. /*
  1708.  *  doKey - handle keypress
  1709.  *
  1710.  */
  1711.  
  1712. static void
  1713. doKey(int key)
  1714. {
  1715.     register TEPtr pTE = *c.hTE;
  1716.     register int selStart = pTE->selStart;
  1717.     register int selEnd = pTE->selEnd;
  1718.     register short len;
  1719.     register char *p;
  1720.  
  1721.     if (!c.reading || c.inverse && key > 0x7F)
  1722.         goto beep;
  1723.     if (c.raw) {
  1724.         *in.ptr++ = key;
  1725.         in.cnt = 0;
  1726.         return;
  1727.     }
  1728.     
  1729.         /*  process control characters  */
  1730.         
  1731.     if (key < ' ') {
  1732.         switch (key) {
  1733.             case '\25':                        /*  ^U  */
  1734.             case '\33':                        /*  escape, clear  */
  1735.                 in.cnt += in.ptr - in.buf;
  1736.                 in.ptr = in.buf;
  1737.                 selStart = in.min;
  1738.                 selEnd = in.max;
  1739.                 goto blank;
  1740.             case '\b':                        /*  backspace  */
  1741.                 if (c.edit)
  1742.                     goto blank;
  1743.                 if (c.cbreak)
  1744.                     goto buffer;
  1745.                 if (in.ptr == in.buf)
  1746.                     goto beep;
  1747.                 --in.ptr;
  1748.                 ++in.cnt;
  1749.                 goto cursor;
  1750.             case '\34':                        /*  left arrow  */
  1751.                 if (selStart == selEnd)
  1752.                     --selStart;
  1753.                 goto cursor;
  1754.             case '\35':                        /*  right arrow  */
  1755.                 if (selStart == selEnd)
  1756.                     ++selEnd;
  1757.                 selStart = selEnd;
  1758.                 goto cursor;
  1759.             case '\36':                        /*  up arrow  */
  1760.                 selStart = in.min;
  1761.                 goto cursor;
  1762.             case '\37':                        /*  down arrow  */
  1763.             case '\t':                        /*  tab  */
  1764.                 selStart = in.max;
  1765.                 goto cursor;
  1766.             case '\4':                        /*  ^D  */
  1767.             case '\r':                        /*  return  */
  1768.             case '\3':                        /*  enter  */
  1769.                 if (len = in.max - in.min) {
  1770.                     p = *pTE->hText + in.min;
  1771.                     asm {
  1772.                         movea.l    in.ptr,a0
  1773.                         bra.s    @2
  1774. @1                        move.b    (p)+,(a0)+
  1775. @2                        dbra    len,@1
  1776.                         move.l    a0,in.ptr
  1777.                     }
  1778.                 }
  1779.                 if (key != '\4')
  1780.                     *in.ptr++ = '\n';
  1781.                 newline();
  1782.                 in.cnt = 0;
  1783.                 break;
  1784.         }
  1785.         return;
  1786.     }
  1787.     
  1788.         /*  delete any existing selection  */
  1789.  
  1790. blank:
  1791.     if (c.edit) {
  1792.         if (selStart == selEnd) {
  1793.             if (key != '\b')
  1794.                 goto insert;
  1795.             --selStart;
  1796.         }
  1797.         if (selStart < in.min || selEnd > in.max)
  1798.             goto beep;
  1799.         blank(selStart, selEnd);
  1800.     }
  1801.  
  1802.         /*  insert the key  */
  1803.         
  1804. insert:
  1805.     if (key >= ' ') {
  1806.         if (in.max - in.min == in.cnt - 1)
  1807.             SysBeep(2);
  1808.         else if (c.edit)
  1809.             insert(key, selStart++);
  1810.         else {
  1811. buffer:        *in.ptr++ = key;
  1812.             if (c.cbreak) {
  1813.                 output(in.ptr - 1, 1);
  1814.                 in.cnt = 0;
  1815.                 return;
  1816.             }
  1817.             --in.cnt;
  1818.         }
  1819.     }
  1820.     
  1821.         /*  update the cursor  */
  1822.  
  1823. cursor:
  1824.     if (selStart > in.max)
  1825.         selStart = in.max;
  1826.     else if (selStart < in.min)
  1827.         selStart = in.min;
  1828.     setcursor(selStart - in.min);
  1829.     return;
  1830.     
  1831.         /*  beep only  */
  1832.  
  1833. beep:
  1834.     SysBeep(2);
  1835. }
  1836.  
  1837.  
  1838. /*
  1839.  *  doCut - handle "cut" command
  1840.  *
  1841.  *  Selected text (if any) is placed in the desk scrap, then deleted.
  1842.  *
  1843.  */
  1844.  
  1845. static void
  1846. doCut(void)
  1847. {
  1848.     register TEPtr pTE = *c.hTE;
  1849.     
  1850.     if (pTE->active && pTE->selStart < pTE->selEnd) {
  1851.         if (!c.reading || pTE->selStart < in.min || pTE->selEnd > in.max)
  1852.             SysBeep(2);
  1853.         else {
  1854.             doCopy();
  1855.             doKey('\b');
  1856.         }
  1857.     }
  1858. }
  1859.  
  1860.  
  1861. /*
  1862.  *  doCopy - handle "copy" command
  1863.  *
  1864.  *  Selected text (if any) is placed in the desk scrap.
  1865.  *
  1866.  */
  1867.  
  1868. static void
  1869. doCopy(void)
  1870. {
  1871.     register TEPtr pTE = *c.hTE;
  1872.     
  1873.     if (pTE->active && pTE->selStart < pTE->selEnd) {
  1874.         TECopy(c.hTE);
  1875.         ZeroScrap();
  1876.         PutScrap(TEScrpLength, 'TEXT', copy());
  1877.         endcopy();
  1878.     }
  1879. }
  1880.  
  1881.  
  1882. /*
  1883.  *  doPaste - handle "paste" command
  1884.  *
  1885.  *  Pasted text is obtained from the desk scrap and stored in the paste
  1886.  *  buffer, "c.pasteH".  ProcessEvent will take characters from the buffer
  1887.  *  in preference to the keyboard.
  1888.  *
  1889.  */
  1890.  
  1891. static void
  1892. doPaste(void)
  1893. {
  1894.     if (!c.reading || (**c.hTE).selStart < in.min || (**c.hTE).selEnd > in.max) {
  1895.         SysBeep(2);
  1896.         return;
  1897.     }
  1898.     if ((c.pasteLen = GetScrap(TEScrpHandle, 'TEXT', &c.pasteOfs)) > 0) {
  1899.         c.pasteH = TEScrpHandle;
  1900.         TEScrpHandle = NewHandle(0);
  1901.         c.pasteOfs = 0;
  1902.     }
  1903.     TEScrpLength = 0;
  1904. }
  1905.  
  1906.  
  1907. /* ---------- printing ---------- */
  1908.  
  1909.  
  1910. /*
  1911.  *  print_console - print the echo file
  1912.  *
  1913.  */
  1914.  
  1915. static void
  1916. print_console(void)
  1917. {
  1918.         /*  check for availability of printing  */
  1919.  
  1920. #ifdef __PRINTTRAPS__
  1921.     if (GetTrapAddress(0xA8FD) == GetTrapAddress(0xA89F)) {
  1922.         c.echo2fp->remove = 0;
  1923.         return;
  1924.     }
  1925. #endif
  1926.     
  1927.         /*  print away  */
  1928.         
  1929.     if (!noPrint) {
  1930.         PrOpen();
  1931.         if (!PrError()) {
  1932.             print();
  1933.             PrClose();
  1934.         }
  1935.     }
  1936. }
  1937.  
  1938.  
  1939. /*
  1940.  *  print - spool file to printer
  1941.  *
  1942.  */
  1943.  
  1944. static void
  1945. print(void)
  1946. {
  1947.     static THPrint hPrint;
  1948.     THPrint h;
  1949.     GrafPtr savePort;
  1950.     TPPrPort port;
  1951.     register TEPtr pTE;
  1952.     int ascent, nlines, i;
  1953.     Rect pageRect;
  1954.     Point where;
  1955.     char buf[BUFSIZ];
  1956.     TPrStatus status;
  1957.     
  1958.         /*  set up job record  */
  1959.     
  1960.     h = (THPrint) NewHandle(sizeof(TPrint));
  1961.     PrintDefault(h);
  1962.     if (hPrint) {
  1963.         PrJobMerge(hPrint, h);
  1964.         DisposHandle((Handle)hPrint);
  1965.     }
  1966.     else {
  1967.         InitCursor();
  1968.         if (!PrJobDialog(h)) {
  1969.             noPrint = 1;
  1970.             return;
  1971.         }
  1972.     }
  1973.     hPrint = h;
  1974.     
  1975.         /*  set up printing port  */
  1976.     
  1977.     GetPort(&savePort);
  1978.     port = PrOpenDoc(h, NULL, NULL);
  1979.     pTE = *c.hTE;
  1980.     TextFont(pTE->txFont);
  1981.     TextSize(pTE->txSize);
  1982.     TextFace(pTE->txFace);
  1983.     ascent = pTE->fontAscent;
  1984.     pageRect = (**h).prInfo.rPage;
  1985.     nlines = (pageRect.bottom - pageRect.top) / c.height;
  1986.     where.h = pageRect.left + 36;    /* leave 1/2 inch margin */
  1987.  
  1988.         /*  print each page  */
  1989.  
  1990.     rewind(c.echo2fp);
  1991.     c.echo2fp->binary = 0;
  1992.     do {
  1993.         PrOpenPage(port, NULL);
  1994.         where.v = pageRect.top + ascent;
  1995.         for (i = 0; i < nlines && fgets(buf, sizeof buf, c.echo2fp); i++) {
  1996.             MoveTo(where.h, where.v);
  1997.             DrawText(buf, 0, strlen(buf) - 1);
  1998.             where.v += c.height;
  1999.         }
  2000.         PrClosePage(port);
  2001.     } while (!PrError() && !feof(c.echo2fp));
  2002.     
  2003.         /*  done printing  */
  2004.     
  2005.     PrCloseDoc(port);
  2006.     SetPort(savePort);
  2007.     if ((**h).prJob.bJDocLoop == bSpoolLoop && !PrError())
  2008.         PrPicFile(h, NULL, NULL, NULL, &status);
  2009. }
  2010.